/*
 * gVirtuS -- A GPGPU transparent virtualization component.
 *
 * Copyright (C) 2009-2010  The University of Napoli Parthenope at Naples.
 *
 * This file is part of gVirtuS.
 *
 * gVirtuS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * gVirtuS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with gVirtuS; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Written by: Giuseppe Coviello <giuseppe.coviello@uniparthenope.it>,
 *             Department of Applied Science
 */

/**
 * @file   Buffer.cpp
 * @author Giuseppe Coviello <giuseppe.coviello@uniparthenope.it>
 * @date   Sun Oct 18 13:16:46 2009
 *
 * @brief
 *
 *
 */
//#define DEBUG
#include "gvirtus/communicators/Buffer.h"

using namespace std;
using gvirtus::communicators::Buffer;

Buffer::Buffer(size_t initial_size, size_t block_size) {
  mSize = initial_size;
  mBlockSize = block_size;
  mLength = 0;
  mOffset = 0;
  mpBuffer = NULL;
  mOwnBuffer = true;
  if (mSize == 0) mSize = 0;
  if ((mSize = (mSize / mBlockSize) * mBlockSize) == 0) mSize = mBlockSize;
  if ((mpBuffer = (char *)malloc(mSize)) == NULL)
    throw "Can't allocate memory.";
  mBackOffset = mLength;
}

Buffer::Buffer(const Buffer &orig) {
  mBlockSize = orig.mBlockSize;
  mLength = orig.mLength;
  mSize = orig.mLength;
  mOffset = orig.mOffset;
  mLength = orig.mLength;
  mOwnBuffer = true;
  if ((mpBuffer = (char *)malloc(mSize)) == NULL)
    throw "Can't allocate memory.";
  memmove(mpBuffer, orig.mpBuffer, mLength);
  mBackOffset = mLength;
}

Buffer::Buffer(istream &in) {
  in.read((char *)&mSize, sizeof(size_t));
  mBlockSize = BLOCK_SIZE;
  mLength = mSize;
  mOffset = 0;
  mOwnBuffer = true;
  if ((mpBuffer = (char *)malloc(mSize)) == NULL)
    throw "Can't allocate memory.";
  in.read(mpBuffer, mSize);
  mBackOffset = mLength;
}

Buffer::Buffer(char *buffer, size_t buffer_size, size_t block_size) {
  mSize = buffer_size;
  mBlockSize = block_size;
  mLength = mSize;
  mOffset = 0;
  mpBuffer = buffer;
  mOwnBuffer = false;
  mBackOffset = mLength;
}

Buffer::~Buffer() {
  if (mOwnBuffer) free(mpBuffer);
}

void Buffer::Reset() {
  mLength = 0;
  mOffset = 0;
  mBackOffset = 0;
}

void Buffer::Reset(Communicator *c) {
  c->Read((char *)&mLength, sizeof(size_t));
#ifdef DEBUG
  std::cout << "readed size of buffer " << mLength << std::endl;
#endif
  mOffset = 0;
  mBackOffset = mLength;
  if (mLength >= mSize) {
    mSize = (mLength / mBlockSize + 1) * mBlockSize;
    if ((mpBuffer = (char *)realloc(mpBuffer, mSize)) == NULL)
      throw "Can't reallocate memory.";
  }
  c->Read(mpBuffer, mLength);
}

const char *const Buffer::GetBuffer() const { return mpBuffer; }

size_t Buffer::GetBufferSize() const { return mLength; }

void Buffer::Dump(Communicator *c) const {
  /**
   *  TO-DO scrivi al message dispatcher che stai per scrivere
   *  acquisisci il lock
   *  scrivi
   *  md->write(communicator out, tid, mpBuffer, mLenght);
   */
  c->Write((char *)&mLength, sizeof(size_t));
  c->Write(mpBuffer, mLength);
  c->Sync();

  /**
   * TO-DO rilascia il lock
   * notifica
   *
   */
}
